Une zone locale home.arpa signée avec DNSSEC

 

Pour s'auto-authentifier

Ce billet fait suite à une question posée sur le fédivers : comment profiter d'une zone home.arpa. à soi et d'enregistrements SSHFP pour faciliter la gestion de SSH sur le réseau local. Question non triviale car SSHFP nécessite DNSSEC. Voyons donc comment signer une zone locale. La technique va impliquer 2 outils supplémentaires en plus d'Unbound : NSD pour servir la zone locale et ldns pour gérer DNSSEC (génération de clés, signature de la zone). Pour ce billet, je ne rentrerai pas dans les détails de DNSSEC (ZSK, KSK, DS...), une connaissance préalable de la chose est donc préférable.

Fichier de zone

Donc, ce n'est plus Unbound qui va servir la zone locale, mais NSD, un vrai serveur faisant autorité. La première étape va donc être de nettoyer la configuration Unbound pour y enlever les paramètres local-zonelocal-data,... (si présents) et installer NSD. On édite un fichier de configuration dans /etc/nsd/nsd.conf.d/

server:

                        interface: 127.0.0.1:53530 # On n'écoute que localement et surtout pas sur le port 53

                zone:

                        name: "home.arpa"

                        zonefile: "/etc/nsd/nsd.conf.d/home.arpa.zone.signed"

Le fichier de zone n'existe pas encore, fabriquons le. Avant d'avoir un fichier de zone signé, il faut un fichier... sans signatures. Il devrait ressembler à quelque chose comme cela :

$TTL 86400

                $ORIGIN home.arpa.

                @       14400   IN      SOA     ns.home.arpa.   hostmaster.home.arpa. (

                                                1       ; Serial

                                                4H      ; refresh after 4 hour

                                                1H      ; retry after 1 hour

                                                3W      ; expire after 3 weeks

                                                1H)     ; minimun TTL of 1 hour

 

                ;IPv4

                router          IN      A       192.168.0.1

                cthulhu         IN      A       192.168.0.2

                eth.azathoth    IN      A       192.168.0.3

                wifi.azathoth   IN      A       192.168.0.4

 

                ;IPv6

                router          IN      AAAA    2001:db8:1a1a:ca11::1

                cthulhu         IN      AAAA    2001:db8:1a1a:ca11:0f:c700:100:1926

 

                ;Divers

                azathoth        IN      TXT     "Une adresse par interface"

Quelques remarques :

DNSSEC

C'est donc ldns qui va gérer la partie DNSSEC. C'est une boîte à outils très utile, notamment pour une gestion artisanale de ses zones signées (pour industrialiser, on utiliserait OpenDNSSEC). Sous Debian, il s'agit du paquet ldnsutils. On l'installe :

                # apt-get install ldns-utils

Les 2 outils qui nous intéressent sont ldns-keygen et ldns-signzone. Les noms ont l'avantage d'être explicites.

Génération des clés

Il faut générer une ZSK et une KSK. Commençons par la ZSK. Avant cela, il faut choisir un algorithme. ldns est dépendant d'OpenSSL de ce point de vue là. Pour connaître la liste supportée par ldns, on entre :

                # ldns-keygen -a list

                Possible algorithms:

                RSAMD5

                RSASHA1

                RSASHA1-NSEC3-SHA1

                RSASHA256

                RSASHA512

                ECDSAP256SHA256

                ECDSAP384SHA384

                ED25519

                ED448

                DSA

                DSA-NSEC3-SHA1

                hmac-md5.sig-alg.reg.int

                hmac-sha1

                hmac-sha256

                hmac-sha224

                hmac-sha384

                hmac-sha512

Pour ce billet, on prendra un algorithme répandu : ECDSAP256SHA256. Une précision importante : ldns n'a pas d'option pour indiquer le répertoire où écrire les clés générées, il le fait automatiquement dans le répertoire courant. Pensez à bien se positionner avant la génération. Cette étape génera jusqu'à 3 fichiers pour chaque clé :

Générons donc notre ZSK

                # ldns-keygen -a ECDSAP256SHA256 home.arpa

                Khome.arpa.+013+53551

Puis la KSK. Même commande, on ajoute uniquement le paramètre -k

                # ldns-keygen -k -a ECDSAP256SHA256 home.arpa

                Khome.arpa.+013+18062

On vérifie que l'on a tout :

                # ls Khome.arpa.+013+*

                Khome.arpa.+013+18062.ds  Khome.arpa.+013+18062.key  Khome.arpa.+013+18062.private  Khome.arpa.+013+53551.key  Khome.arpa.+013+53551.private

Nous avons donc une clé avec l'id 18062

                cat Khome.arpa.+013+18062.key

                home.arpa.      IN      DNSKEY  257 3 13 bOcYev/grRO3AOc/mtc+VJNIsfpzTP2da1LhUreOrMUViE1CGY0wye1WXsAAqHIWBD0z5EtZCKV6qS0Y8OOg2g== ;{id = 18062 (ksk), size = 256b}

On a le drapeau 257, c'est bien une KSK. Vérifions la clé avec l'id 53551

                # cat Khome.arpa.+013+53551.key

                home.arpa.      IN      DNSKEY  256 3 13 l+GT51VQ/PgJuJUXr3j6RkXqyDSCj1djhK+HOyoviKQnqmRi5Qs/6m71jsQ4lxj9caVXC0KlJB1OMove0keDkw== ;{id = 53551 (zsk), size = 256b}

On a le drapeau 256, c'est bien une ZSK. Les indications en commentaires sont correctes, ldns a bien fait son travail.

Signature de la zone

Pour la signature, nous allons simplifier la procédure au maximum en ne s'occupant pas de NSEC (et NSEC3), ldns générant par défaut des enregistrement NSEC. Pour la durée de vie des signatures, 2 possibiltés :

On signe donc notre zone :

                # ldns-signzone -e $(($(date +%s) + 315365000)) -o home.arpa -f /etc/nsd/nsd.conf.d/home.arpa.zone.signed /etc/nsd/nsd.conf.d/home.arpa.zone Khome.arpa.+013+53551 Khome.arpa.+013+18062

S'il n'y a pas de messages d'erreurs, vérifions le fichier généré :

                # cat /etc/nsd/nsd.conf.d/home.arpa.zone.signed

                home.arpa.      14400   IN      SOA     ns.home.arpa. hostmaster.home.arpa. 1 14400 3600 1814400 3600

                home.arpa.      14400   IN      RRSIG   SOA 13 2 14400 20301112033940 20201114021620 53551 home.arpa. u7KPfzsPJXeal/24cdLbkCpCQzcb25Sn+Dch6yjIaKYv5M9/8b6pHR/XWVcvTUNVKL/P01FT1LtcrSEyTABKkw==

                home.arpa.      14400   IN      DNSKEY  256 3 13 l+GT51VQ/PgJuJUXr3j6RkXqyDSCj1djhK+HOyoviKQnqmRi5Qs/6m71jsQ4lxj9caVXC0KlJB1OMove0keDkw== ;{id = 53551 (zsk), size = 256b}

                home.arpa.      14400   IN      DNSKEY  257 3 13 bOcYev/grRO3AOc/mtc+VJNIsfpzTP2da1LhUreOrMUViE1CGY0wye1WXsAAqHIWBD0z5EtZCKV6qS0Y8OOg2g== ;{id = 18062 (ksk), size = 256b}

                home.arpa.      14400   IN      RRSIG   DNSKEY 13 2 14400 20301112033940 20201114021620 18062 home.arpa. 85j6NrPruCrPcytBe4ckfse9EnCfUfYeMjHQ69+chYtvk6J0wlmAF1SraSS/wgCWx2FciFhCMdzCYAjUr7kfjQ==

                home.arpa.      3600    IN      NSEC    azathoth.home.arpa. SOA RRSIG NSEC DNSKEY

                ...

Tout semble bon. On peut relancer NSD.

                # systemctl restart nsd

Si dans le futur vous modifiez la zone :

                # nsd-control reload home.arpa

Configuration d'Unbound

Le plus dur est fait, reste à configurer le résolveur. Il y a 2 éléments à passer à Unbound :

Pour ce dernier paramètre, on récupère l'enregistrement DS précédemment généré

                # cat Khome.arpa.+013+18062.ds

                home.arpa.      IN      DS      18062 13 2 4eda0e1d8f64c986f3f1e5bb1f2ba834af9847955b0b1a8d5fc29c1e0cb9791a

On le copie, et on configure Unbound

server:

                ...

                        trust-anchor: "home.arpa.      IN      DS      18062 13 2 4eda0e1d8f64c986f3f1e5bb1f2ba834af9847955b0b1a8d5fc29c1e0cb9791a"

 

                stub-zone:

                        name: "home.arpa."

                        stub-addr: 127.0.0.1@53530

On le redémarre et on teste

                # dig cthulhu.home.arpa

 

                ; <<>> DiG 9.16.6-Debian <<>> cthulhu.home.arpa

                ;; global options: +cmd

                ;; Got answer:

                ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6043

                ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

 

                ;; OPT PSEUDOSECTION:

                ; EDNS: version: 0, flags:; udp: 1232

                ;; QUESTION SECTION:

                ;test.home.arpa.                        IN      A

 

                ;; ANSWER SECTION:

                cthulhu.home.arpa.         86400   IN      A       192.168.0.2

 

                ;; Query time: 0 msec

                ;; SERVER: 127.0.0.1#53(127.0.0.1)

                ;; WHEN: sam. nov. 14 03:31:05 CET 2020

                ;; MSG SIZE  rcvd: 59

On a bien le bit AD (Authentificated Data), indiquant que la réponse est validée. Tout fonctionne bien donc. Une dernière chose : sur les systèmes récents, disposant de la libc6 2.31 (et supérieure), le système ne transmet plus automatiquement le bit AD aux applications qui en ont besoin (c'est a priori le cas de SSH lorsqu'on utilise SSHFP — à tester ceci dit, n'utilisant pas SSHFP — ou gnutls-cli lorsque l'on essaye d'utiliser DANE). Pour que les applications disposent de ce bit, il faut ajouter l'option suivante à votre resolv.conf :

options trust-ad

Ajoutons que ce nouveau comportement par défaut de libc6 est pensé pour les environnement où le résolveur n'est pas forcément fiable. S'il tourne localement, ce n'est normalement pas le cas (à moins de ne pas se faire confiance ☺).